/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package string;

import geneticprogramming.problem.GEProblem;
import geneticprogramming.problem.Individual;

/**
 *
 * @author CJ
 */
public class StringProblem implements GEProblem{

    protected String desired;
    protected double idealFitness;

    protected double slope;
    protected double maxMutate;

    public StringProblem(String desired, double maxMutate, double minMutate){
        this.desired = desired;
        this.idealFitness = getFitness(new StringIndividual(desired));

        this.maxMutate = maxMutate;
        this.slope = (maxMutate - minMutate)/-idealFitness;
    }

    public Individual[] getRandomPopulation(int populationNum){
        StringIndividual[] result = new StringIndividual[populationNum];
        for(int i = 0; i < result.length; ++i){
            char[] temp = new char[desired.length()];
            for(int j = 0; j < temp.length; ++j){
                temp[j] = getRandomChar();
            }
            result[i] = new StringIndividual(new String(temp));
        }
        return result;
    }

    public Individual mutate(Individual individual){
        try{
            char[] input = ((String)individual.getState()).toCharArray();

           // if(input.length != desired.length())
            //    throw new IllegalArgumentException("Lenght of Strings are different.");

            int place = (int) (Math.random() * desired.length());
            char change = getRandomChar();

            input[place] = change;
            return new StringIndividual(new String(input));
        }catch(Exception e){
            e.printStackTrace();
            throw new IllegalArgumentException("Illegal Arguement passed to mutate.");
        }
    }
    
    public Individual mate(Individual invOne, Individual invTwo){
        try{
            char[] indiviOne = ((String)invOne.getState()).toCharArray();
            char[] indiviTwo = ((String) invTwo.getState()).toCharArray();

            //This will slow you down
           // if(indiviOne.length != indiviTwo.length ||
            //        indiviOne.length != desired.length() ||
             //       indiviTwo.length != desired.length())
              //  throw new IllegalArgumentException("Length of Strings are different.");

            int splicePoint = (int)(Math.random() * desired.length());

            char[] result = new char[desired.length()];


            //Switch that determines the order of copying
            switch((int)(Math.random() * 2)){
                case 0:
                    //Inidividual One first
                    for(int i = 0; i < splicePoint; ++i)
                        result[i] = indiviOne[i];
                    for(int i = splicePoint; i < desired.length(); ++i)
                        result[i] = indiviTwo[i];
                    break;
                case 1:
                    //Inidividual Two first
                    for(int i = 0; i < splicePoint; ++i)
                        result[i] = indiviTwo[i];
                    for(int i = splicePoint; i < desired.length(); ++i)
                        result[i] = indiviOne[i];
                    break;
            }
            return new StringIndividual(new String(result));
        }catch(Exception e){
            e.printStackTrace();
            throw new IllegalArgumentException("Illegal Argument passed to mate.");
        }
    }
    
    public double getFitness(Individual individual){
        try{
            char[] input = ((String) ((StringIndividual) individual).getState()).toCharArray();

           // if(input.length != desired.length())
            //    throw new IllegalArgumentException("Lengths of Strings are different.");

            double result = 1;

            for(int i = 0; i < desired.length(); ++i)
                if(input[i] == desired.charAt(i))
                    result++;

            return result;
        }catch(Exception e){
            e.printStackTrace();
            throw new IllegalArgumentException("Illegal Argument passed to getFitness.");
        }
    }
    public double getPerfectFitness(){
        return this.idealFitness;
    }

    /**
     * Currently using a linear method of getting the mutation probability.
     * @param averageFitness
     * @return
     */
    public int getMutationProb(double averageFitness){
        return (int)(this.slope * (averageFitness - 1) + this.maxMutate);
    }

    protected char getRandomChar(){

        int random = ((int)(Math.random() * 55));
        if(random <= 25)
            return (char)(random + 65);
        else if(random <= 51)
            return (char)(random +71);
        else if(random <= 52)
            return ' ';
        else if(random <= 53)
            return ',';
        else
            return '.';
//        return (char)((int)(Math.random() * 94 + 32));
    }

}
